package com.azier.kapacitor.util;

import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串工具类
 *
 * @author 金建强(ptma@163.com)
 * @version 2015-11-11 17:07.
 */
public final class StringUtil {

    public static final String NULL       = "null";
    public static final String AT_NULL    = "@null";
    public static final String SPACE      = " ";
    public static final String DOT        = ".";
    public static final String SLASH      = "/";
    public static final String BACKSLASH  = "\\";
    public static final String EMPTY      = "";
    public static final String CRLF       = "\r\n";
    public static final String NEWLINE    = "\n";
    public static final String UNDERLINE  = "_";
    public static final String COMMA      = ",";
    public static final String COLON      = ":";

    public static final String HTML_NBSP  = "&nbsp;";
    public static final String HTML_AMP   = "&amp";
    public static final String HTML_QUOTE = "&quot;";
    public static final String HTML_LT    = "&lt;";
    public static final String HTML_GT    = "&gt;";

    public static final String EMPTY_JSON = "{}";

    private StringUtil(){
    }

    /**
     * 格式化文本
     *
     * @param template 文本模板，被替换的部分用 {} 表示
     * @param values 参数值
     * @return 格式化后的文本
     */
    public static String format(String template, Object... values) {
        if (values == null || values.length == 0 || isBlank(template)) {
            return template;
        }

        final StringBuilder sb = new StringBuilder();
        final int length = template.length();

        int valueIndex = 0;
        char currentChar;
        for (int i = 0; i < length; i++) {
            if (valueIndex >= values.length) {
                sb.append(substring(template, i, length));
                break;
            }

            currentChar = template.charAt(i);
            if (currentChar == '{') {
                final char nextChar = template.charAt(++i);
                if (nextChar == '}') {
                    sb.append(values[valueIndex++]);
                } else {
                    sb.append('{').append(nextChar);
                }
            } else {
                sb.append(currentChar);
            }

        }

        return sb.toString();
    }

    /**
     * 格式化文本
     *
     * @param template 文本模板，被替换的部分用 {key} 表示
     * @param map 参数值对
     * @return 格式化后的文本
     */
    public static String format(String template, Map<?, ?> map) {
        if (null == map || map.isEmpty()) {
            return template;
        }

        for (Map.Entry<?, ?> entry : map.entrySet()) {
            template = template.replace("{" + entry.getKey() + "}", String.valueOf(entry.getValue()));
        }
        return template;
    }

    /**
     * 替换所有子串为另一个子串.
     *
     * @param s 源字符串
     * @param sub 待替换的字符窜
     * @param with 要替换成的字符窜
     */
    public static String replace(String s, String sub, String with) {
        int c = 0;
        int i = s.indexOf(sub, c);
        if (i == -1) {
            return s;
        }
        int length = s.length();
        StringBuilder sb = new StringBuilder(length + with.length());
        do {
            sb.append(s.substring(c, i));
            sb.append(with);
            c = i + sub.length();
        } while ((i = s.indexOf(sub, c)) != -1);
        if (c < length) {
            sb.append(s.substring(c, length));
        }
        return sb.toString();
    }

    /**
     * 替换所有字符为另一个字符.
     *
     * @param s 源字符串
     * @param sub 待替换的字符
     * @param with 要替换成的字符
     */
    public static String replaceChar(String s, char sub, char with) {
        int startIndex = s.indexOf(sub);
        if (startIndex == -1) {
            return s;
        }
        char[] str = s.toCharArray();
        for (int i = startIndex; i < str.length; i++) {
            if (str[i] == sub) {
                str[i] = with;
            }
        }
        return new String(str);
    }

    /**
     * 批量替换所有字符为另外的字符，按索引对应.
     *
     * @param s 源字符窜
     * @param sub 待替换字符数组
     * @param with 要替换成的字符数组
     */
    public static String replaceChars(String s, char[] sub, char[] with) {
        char[] str = s.toCharArray();
        for (int i = 0; i < str.length; i++) {
            char c = str[i];
            for (int j = 0; j < sub.length; j++) {
                if (c == sub[j]) {
                    str[i] = with[j];
                    break;
                }
            }
        }
        return new String(str);
    }

    /**
     * 替换第一次出现的字符串.
     *
     * @param s 源字符串
     * @param sub 待替换的字符窜
     * @param with 要替换成的字符串
     */
    public static String replaceFirst(String s, String sub, String with) {
        int i = s.indexOf(sub);
        if (i == -1) {
            return s;
        }
        return s.substring(0, i) + with + s.substring(i + sub.length());
    }

    /**
     * 替换第一次出现的字符.
     *
     * @param s 源字符串
     * @param sub 待替换的字符
     * @param with 要替换成的字符
     */
    public static String replaceFirst(String s, char sub, char with) {
        int index = s.indexOf(sub);
        if (index == -1) {
            return s;
        }
        char[] str = s.toCharArray();
        str[index] = with;
        return new String(str);
    }

    /**
     * 替换最后一个出现的字符窜.
     *
     * @param s 源字符串
     * @param sub 待替换的字符串
     * @param with 要替换成的字符串
     */
    public static String replaceLast(String s, String sub, String with) {
        int i = s.lastIndexOf(sub);
        if (i == -1) {
            return s;
        }
        return s.substring(0, i) + with + s.substring(i + sub.length());
    }

    /**
     * 替换最后一个出现的字符.
     *
     * @param s 源字符串
     * @param sub 待替换的字符
     * @param with 要替换成的字符
     */
    public static String replaceLast(String s, char sub, char with) {
        int index = s.lastIndexOf(sub);
        if (index == -1) {
            return s;
        }
        char[] str = s.toCharArray();
        str[index] = with;
        return new String(str);
    }

    // ---------------------------------------------------------------- remove

    /**
     * 移除所有出现的特定子字符串.
     *
     * @param s 源字符串
     * @param sub 要移除的特定子字符串
     */
    public static String remove(String s, String sub) {
        int c = 0;
        int sublen = sub.length();
        if (sublen == 0) {
            return s;
        }
        int i = s.indexOf(sub, c);
        if (i == -1) {
            return s;
        }
        StringBuilder sb = new StringBuilder(s.length());
        do {
            sb.append(s.substring(c, i));
            c = i + sublen;
        } while ((i = s.indexOf(sub, c)) != -1);
        if (c < s.length()) {
            sb.append(s.substring(c, s.length()));
        }
        return sb.toString();
    }

    /**
     * 从源字符串中移除给出字符串中包含的所有字符.
     *
     * @param src 源字符串
     * @param chars 含所有待移除字符的字符串
     */
    public static String removeChars(String src, String chars) {
        int i = src.length();
        StringBuilder sb = new StringBuilder(i);
        for (int j = 0; j < i; j++) {
            char c = src.charAt(j);
            if (chars.indexOf(c) == -1) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 从字符串中移除字符.
     *
     * @param src 源字符串
     * @param chars 待移除的字符集
     */
    public static String removeChars(String src, char... chars) {
        int i = src.length();
        StringBuilder sb = new StringBuilder(i);
        mainloop: for (int j = 0; j < i; j++) {
            char c = src.charAt(j);
            for (char aChar : chars) {
                if (c == aChar) {
                    continue mainloop;
                }
            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * 从字符窜移除特定字符.
     *
     * @param string 源字符串
     * @param ch 要移除的字符
     */
    public static String remove(String string, char ch) {
        int stringLen = string.length();
        char[] result = new char[stringLen];
        int offset = 0;

        for (int i = 0; i < stringLen; i++) {
            char c = string.charAt(i);

            if (c == ch) {
                continue;
            }

            result[offset] = c;
            offset++;
        }

        if (offset == stringLen) {
            return string; // 没有变化
        }

        return new String(result, 0, offset);
    }

    /**
     * 大写首字母<br>
     * 例如：str = name, return Name
     *
     * @param str 字符串
     * @return 字符串
     */
    public static String upperFirst(String str) {
        if (isBlank(str)) {
            return str;
        }
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }

    /**
     * 小写首字母<br>
     * 例如：str = Name, return name
     *
     * @param str 字符串
     * @return 字符串
     */
    public static String lowerFirst(String str) {
        if (isBlank(str)) {
            return str;
        }
        return Character.toLowerCase(str.charAt(0)) + str.substring(1);
    }

    /**
     * 比较两个对象，允许两个对象都为 null
     */
    public static boolean equals(Object obj1, Object obj2) {
        return (obj1 != null) ? (obj1.equals(obj2)) : (obj2 == null);
    }

    /**
     * 判断字符串是否为空或空白 (<code>null</code> 或者长度为0).
     */
    public static boolean isEmpty(Object str) {
        return (str == null || "".equals(str));
    }

    /**
     * 是否包含空字符串
     */
    public static boolean hasEmpty(String... strs) {
        if (CollectionUtil.isEmpty(strs)) {
            return true;
        }

        for (String str : strs) {
            if (isEmpty(str)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断字符串数组是否全部为空或空白. {@link #isEmpty(Object)}.
     */
    public static boolean isAllEmpty(String... strings) {
        for (String string : strings) {
            if (!isEmpty(string)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断字符串是否不为空(<code>!null</code> 并且长度大于0).
     */
    public static boolean isNotEmpty(Object str) {
        return !isEmpty(str);
    }

    /**
     * 判断字符串是否为空白(<code>null</code> 或仅包含空白字符).
     */
    public static boolean isBlank(String string) {
        return ((string == null) || containsOnlyWhitespaces(string));
    }

    /**
     * 是否包含空字符串
     */
    public static boolean hasBlank(String... strs) {
        if (CollectionUtil.isEmpty(strs)) {
            return true;
        }

        for (String str : strs) {
            if (isBlank(str)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断字符串是否不为空白.
     */
    public static boolean isNotBlank(String string) {
        return ((string != null) && !containsOnlyWhitespaces(string));
    }

    /**
     * 判断字符窜数组是否全部为空白.
     */
    public static boolean isAllBlank(String... strings) {
        for (String string : strings) {
            if (!isBlank(string)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 清理空白字符
     *
     * @param str 被清理的字符串
     * @return 清理后的字符串
     */
    public static String cleanBlank(String str) {
        if (str == null) {
            return null;
        }

        return str.replaceAll("\\s*", EMPTY);
    }

    /**
     * 字符串是否只包含空白字符.
     */
    public static boolean containsOnlyWhitespaces(CharSequence string) {
        int size = string.length();
        for (int i = 0; i < size; i++) {
            char c = string.charAt(i);
            if (!CharUtil.isWhitespace(c)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 字符串是否只包含数字.
     */
    public static boolean containsOnlyDigits(CharSequence string) {
        int size = string.length();
        for (int i = 0; i < size; i++) {
            char c = string.charAt(i);
            if (!CharUtil.isDigit(c)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 字符串是否只包含数字或加减符号.
     */
    public static boolean containsOnlyDigitsAndSigns(CharSequence string) {
        int size = string.length();
        for (int i = 0; i < size; i++) {
            char c = string.charAt(i);
            if ((!CharUtil.isDigit(c)) && (c != '-') && (c != '+')) {
                return false;
            }
        }
        return true;
    }

    /**
     * 是否包含特定字符，忽略大小写
     */
    public static boolean containsIgnoreCase(String str, String testStr){
        if(null == str){
            return null == testStr;
        }
        return str.toLowerCase().contains(testStr.toLowerCase());
    }

    /**
     * 安全转换 Object 到 String.
     */
    public static String toString(Object value) {
        if (value == null) {
            return null;
        }
        return String.valueOf(value);
    }

    /**
     * 安全转换 Object 到 String. 如果 object 为 <code>null</code> 时返回一个空字符串.
     */
    public static String toSafeString(Object value) {
        if (value == null) {
            return EMPTY;
        }

        return String.valueOf(value);
    }

    /**
     * 钻换 Object 为友好格式的字符窜. 所有数组都会被迭代.
     */
    public static String toPrettyString(Object value) {
        if (value == null) {
            return NULL;
        }

        Class<?> type = value.getClass();

        if (type.isArray()) {
            Class componentType = type.getComponentType();

            if (componentType.isPrimitive()) {
                StringBuilder sb = new StringBuilder();
                sb.append('[');

                if (componentType == int.class) {
                    sb.append(ArrayUtil.toString((int[]) value));
                } else if (componentType == long.class) {
                    sb.append(ArrayUtil.toString((long[]) value));
                } else if (componentType == double.class) {
                    sb.append(ArrayUtil.toString((double[]) value));
                } else if (componentType == float.class) {
                    sb.append(ArrayUtil.toString((float[]) value));
                } else if (componentType == boolean.class) {
                    sb.append(ArrayUtil.toString((boolean[]) value));
                } else if (componentType == short.class) {
                    sb.append(ArrayUtil.toString((short[]) value));
                } else if (componentType == byte.class) {
                    sb.append(ArrayUtil.toString((byte[]) value));
                } else {
                    throw new IllegalArgumentException();
                }
                sb.append(']');
                return sb.toString();
            } else {
                StringBuilder sb = new StringBuilder();
                sb.append('[');

                Object[] array = (Object[]) value;
                for (int i = 0; i < array.length; i++) {
                    if (i > 0) {
                        sb.append(',');
                    }
                    sb.append(toPrettyString(array[i]));
                }
                sb.append(']');
                return sb.toString();
            }
        } else if (value instanceof Iterable) {
            Iterable iterable = (Iterable) value;
            StringBuilder sb = new StringBuilder();
            sb.append('{');
            int i = 0;
            for (Object o : iterable) {
                if (i > 0) {
                    sb.append(',');
                }
                sb.append(toPrettyString(o));
                i++;
            }
            sb.append('}');
            return sb.toString();
        }

        return String.valueOf(value);
    }

    /**
     * 转换对象为字符串数组, 如果输入的是数组, 那么数组中的每一个对象都会被转成字符串. 如果输入的不是数组, 那么返回一个只包含一个元素的字符串数组.
     */
    public static String[] toStringArray(Object value) {
        if (value == null) {
            return new String[0];
        }
        Class<?> type = value.getClass();

        if (!type.isArray()) {
            return new String[] { value.toString() };
        }

        Class componentType = type.getComponentType();

        if (componentType.isPrimitive()) {
            if (componentType == int.class) {
                return ArrayUtil.toStringArray((int[]) value);
            } else if (componentType == long.class) {
                return ArrayUtil.toStringArray((long[]) value);
            } else if (componentType == double.class) {
                return ArrayUtil.toStringArray((double[]) value);
            } else if (componentType == float.class) {
                return ArrayUtil.toStringArray((float[]) value);
            } else if (componentType == boolean.class) {
                return ArrayUtil.toStringArray((boolean[]) value);
            } else if (componentType == short.class) {
                return ArrayUtil.toStringArray((short[]) value);
            } else if (componentType == byte.class) {
                return ArrayUtil.toStringArray((byte[]) value);
            } else {
                throw new IllegalArgumentException();
            }
        } else {
            return ArrayUtil.toStringArray((Object[]) value);
        }
    }

    // ---------------------------------------------------------------- capitalize

    /**
     * 首字大写. 其他字符大小写不变.
     *
     * @param str 源字符串, 可能为 null
     * @see #uncapitalize(String)
     */
    public static String capitalize(String str) {
        return changeFirstCharacterCase(true, str);
    }

    /**
     * 首字小写. 其他字符大小写不变.
     *
     * @param str 源字符串, 可能为 null
     * @return the 首字小写后的字符串, 源字符串为 <code>null</code> 时返回 null
     * @see #capitalize(String)
     */
    public static String uncapitalize(String str) {
        return changeFirstCharacterCase(false, str);
    }

    private static String changeFirstCharacterCase(boolean capitalize, String string) {
        int strLen = string.length();
        if (strLen == 0) {
            return string;
        }

        char ch = string.charAt(0);
        char modifiedCh;
        if (capitalize) {
            modifiedCh = Character.toUpperCase(ch);
        } else {
            modifiedCh = Character.toLowerCase(ch);
        }

        if (modifiedCh == ch) {
            return string;
        }

        char[] chars = string.toCharArray();
        chars[0] = modifiedCh;
        return new String(chars);
    }

    /**
     * 按照 Java 变量命名规范进行首字小写转换 一般情况下第一个字母会转成小写, 但是有些特殊情形下（第一个字母和第二个字母都为大写）会保持第一个字母的大小状态.
     * <p>
     * 例如 "FooBah" 会转成 "fooBah" , "X" 会转成 "x", 但是 "URL" 依旧是 "URL".
     *
     * @param name 源字符串.
     */
    public static String decapitalize(String name) {
        if (name.length() == 0) {
            return name;
        }
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
            return name;
        }

        char[] chars = name.toCharArray();
        char c = chars[0];
        char modifiedChar = Character.toLowerCase(c);
        if (modifiedChar == c) {
            return name;
        }
        chars[0] = modifiedChar;
        return new String(chars);
    }

    /**
     * 各单词首字母大写
     */
    public static String title(String string) {
        char[] chars = string.toCharArray();

        boolean wasWhitespace = true;

        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];

            if (CharUtil.isWhitespace(c)) {
                wasWhitespace = true;
            } else {
                if (wasWhitespace) {
                    chars[i] = Character.toUpperCase(c);
                } else {
                    chars[i] = Character.toLowerCase(c);
                }
                wasWhitespace = false;
            }
        }

        return new String(chars);
    }

    /**
     * 缩短字符串.
     */
    public static String truncate(String string, int length) {
        if (string.length() > length) {
            string = string.substring(0, length);
        }
        return string;
    }

    /**
     * <p>
     * 根据给定的索引范围截取子字符串
     * </p>
     * <p>
     * 但是, 索引值可以为负数, 这时真正的索引会从尾部开始计算.<br>
     * 例如：<br>
     * <code>substring(1,-1)</code> 会各剪掉开头和结尾的一个字符.<br>
     * 如果 <code>fromIndex</code> 为负数并且 <code>toIndex</code> 为 0, 会返回尾部的一个字符串.
     * </p>
     * <p>
     * 同时, 本方法不会因为索引超出范围而抛出异常.
     * </p>
     */
    public static String substring(String string, int fromIndex, int toIndex) {
        int len = string.length();

        if (fromIndex < 0) {
            fromIndex = len + fromIndex;

            if (toIndex == 0) {
                toIndex = len;
            }
        }

        if (toIndex < 0) {
            toIndex = len + toIndex;
        }

        // safe net

        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (toIndex > len) {
            toIndex = len;
        }
        if (fromIndex >= toIndex) {
            return EMPTY;
        }

        return string.substring(fromIndex, toIndex);
    }

    /**
     * 指定位置是否存在给定的子字符串.
     */
    public static boolean isSubstringAt(String string, String substring, int offset) {
        int len = substring.length();

        int max = offset + len;

        if (max > string.length()) {
            return false;
        }

        int ndx = 0;
        for (int i = offset; i < max; i++, ndx++) {
            if (string.charAt(i) != substring.charAt(ndx)) {
                return false;
            }
        }

        return true;
    }

    /**
     * 给定字符串是否被字符包围
     *
     * @param str 字符串
     * @param prefix 前缀
     * @param suffix 后缀
     * @return 是否包围，空串不包围
     */
    public static boolean isWrap(String str, String prefix, String suffix) {
        return isSurround(str, prefix, suffix);
    }

    /**
     * 给定字符串是否被字符包围
     *
     * @param str 字符串
     * @param prefix 前缀
     * @param suffix 后缀
     * @return 是否包围，空串不包围
     */
    public static boolean isSurround(String str, String prefix, String suffix) {
        if (isBlank(str)) {
            return false;
        }
        if (str.length() < (prefix.length() + suffix.length())) {
            return false;
        }

        return str.startsWith(prefix) && str.endsWith(suffix);
    }

    /**
     * 用分隔符分隔字符串 如果两个分隔符之间没有内容, 会以一个空白字符串来代替. 因此, 返回的数组的长度始终为: 分隔符个数 + 1.
     * <p>
     * 本方法比 <code>String.split()</code> 块很多, 也稍微快于 <code>StringTokenizer</code>.
     *
     * @param src 待分隔的字符串
     * @param delimiter 分隔符
     * @return 分割好的字符串数组
     */
    public static String[] split(String src, String delimiter) {
        int maxparts = (src.length() / delimiter.length()) + 2; // one more for the last
        int[] positions = new int[maxparts];
        int dellen = delimiter.length();

        int i, j = 0;
        int count = 0;
        positions[0] = -dellen;
        while ((i = src.indexOf(delimiter, j)) != -1) {
            count++;
            positions[count] = i;
            j = i + dellen;
        }
        count++;
        positions[count] = src.length();

        String[] result = new String[count];

        for (i = 0; i < count; i++) {
            result[i] = src.substring(positions[i] + dellen, positions[i + 1]);
        }
        return result;
    }

    /**
     * 分隔字符串
     *
     * @param src source to examine
     * @param d string with delimiter characters
     * @return array of tokens
     */
    public static String[] splitc(String src, String d) {
        if ((d.length() == 0) || (src.length() == 0)) {
            return new String[] { src };
        }
        return splitc(src, d.toCharArray());
    }

    /**
     * 分隔字符串
     *
     * @param src source to examine
     * @param delimiters char array with delimiter characters
     * @return array of tokens
     */
    public static String[] splitc(String src, char[] delimiters) {
        if ((delimiters.length == 0) || (src.length() == 0)) {
            return new String[] { src };
        }
        char[] srcc = src.toCharArray();

        int maxparts = srcc.length + 1;
        int[] start = new int[maxparts];
        int[] end = new int[maxparts];

        int count = 0;

        start[0] = 0;
        int s = 0, e;
        if (CharUtil.equalsOne(srcc[0], delimiters)) { // string starts with delimiter
            end[0] = 0;
            count++;
            s = CharUtil.findFirstDiff(srcc, 1, delimiters);
            if (s == -1) { // nothing after delimiters
                return new String[] { EMPTY, EMPTY };
            }
            start[1] = s; // new start
        }
        while (true) {
            // find new end
            e = CharUtil.findFirstEqual(srcc, s, delimiters);
            if (e == -1) {
                end[count] = srcc.length;
                break;
            }
            end[count] = e;

            // find new start
            count++;
            s = CharUtil.findFirstDiff(srcc, e, delimiters);
            if (s == -1) {
                start[count] = end[count] = srcc.length;
                break;
            }
            start[count] = s;
        }
        count++;
        String[] result = new String[count];
        for (int i = 0; i < count; i++) {
            result[i] = src.substring(start[i], end[i]);
        }
        return result;
    }

    /**
     * 分隔字符串
     *
     * @param src source to examine
     * @param delimiter delimiter character
     * @return array of tokens
     */
    public static String[] splitc(String src, char delimiter) {
        if (src.length() == 0) {
            return new String[] { EMPTY };
        }
        char[] srcc = src.toCharArray();

        int maxparts = srcc.length + 1;
        int[] start = new int[maxparts];
        int[] end = new int[maxparts];

        int count = 0;

        start[0] = 0;
        int s = 0, e;
        if (srcc[0] == delimiter) { // string starts with delimiter
            end[0] = 0;
            count++;
            s = CharUtil.findFirstDiff(srcc, 1, delimiter);
            if (s == -1) { // nothing after delimiters
                return new String[] { EMPTY, EMPTY };
            }
            start[1] = s; // new start
        }
        while (true) {
            // find new end
            e = CharUtil.findFirstEqual(srcc, s, delimiter);
            if (e == -1) {
                end[count] = srcc.length;
                break;
            }
            end[count] = e;

            // find new start
            count++;
            s = CharUtil.findFirstDiff(srcc, e, delimiter);
            if (s == -1) {
                start[count] = end[count] = srcc.length;
                break;
            }
            start[count] = s;
        }
        count++;
        String[] result = new String[count];
        for (int i = 0; i < count; i++) {
            result[i] = src.substring(start[i], end[i]);
        }
        return result;
    }

    /**
     * 将连续出现的重复字符压缩为一个.
     */
    public static String compressChars(String s, char c) {
        int len = s.length();
        StringBuilder sb = new StringBuilder(len);
        boolean wasChar = false;
        for (int i = 0; i < len; i++) {
            char c1 = s.charAt(i);
            if (c1 == c) {
                if (wasChar) {
                    continue;
                }
                wasChar = true;
            } else {
                wasChar = false;
            }
            sb.append(c1);
        }
        if (sb.length() == len) {
            return s;
        }
        return sb.toString();
    }

    /**
     * 在给定的区间内查找首次出现某字符串的位置(start, end].
     */
    public static int indexOf(String src, String sub, int startIndex, int endIndex) {
        if (startIndex < 0) {
            startIndex = 0;
        }
        int srclen = src.length();
        if (endIndex > srclen) {
            endIndex = srclen;
        }
        int sublen = sub.length();
        if (sublen == 0) {
            return startIndex > srclen ? srclen : startIndex;
        }

        int total = endIndex - sublen + 1;
        char c = sub.charAt(0);
        mainloop: for (int i = startIndex; i < total; i++) {
            if (src.charAt(i) != c) {
                continue;
            }
            int j = 1;
            int k = i + 1;
            while (j < sublen) {
                if (sub.charAt(j) != src.charAt(k)) {
                    continue mainloop;
                }
                j++;
                k++;
            }
            return i;
        }
        return -1;
    }

    /**
     * 在给定的区间内查找首次出现某字符的位置(start, end].
     */
    public static int indexOf(String src, char c, int startIndex, int endIndex) {
        if (startIndex < 0) {
            startIndex = 0;
        }
        int srclen = src.length();
        if (endIndex > srclen) {
            endIndex = srclen;
        }
        for (int i = startIndex; i < endIndex; i++) {
            if (src.charAt(i) == c) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 在给定的区间内查找首次出现某字符(不区分大小写)的位置(start, end].
     */
    public static int indexOfIgnoreCase(String src, char c, int startIndex, int endIndex) {
        if (startIndex < 0) {
            startIndex = 0;
        }
        int srclen = src.length();
        if (endIndex > srclen) {
            endIndex = srclen;
        }
        c = Character.toLowerCase(c);
        for (int i = startIndex; i < endIndex; i++) {
            if (Character.toLowerCase(src.charAt(i)) == c) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 查找首次出现某字符串的位置（忽略大小写）.
     */
    public static int indexOfIgnoreCase(String src, String subS) {
        return indexOfIgnoreCase(src, subS, 0, src.length());
    }

    /**
     * 从指定的开始位置查找首次出现某字符串的位置（忽略大小写）.
     */
    public static int indexOfIgnoreCase(String src, String subS, int startIndex) {
        return indexOfIgnoreCase(src, subS, startIndex, src.length());
    }

    /**
     * 在给定的区间内查找首次出现某字符串的位置（忽略大小写）(start, end].
     */
    public static int indexOfIgnoreCase(String src, String sub, int startIndex, int endIndex) {
        if (startIndex < 0) {
            startIndex = 0;
        }
        int srclen = src.length();
        if (endIndex > srclen) {
            endIndex = srclen;
        }

        int sublen = sub.length();
        if (sublen == 0) {
            return startIndex > srclen ? srclen : startIndex;
        }
        sub = sub.toLowerCase();
        int total = endIndex - sublen + 1;
        char c = sub.charAt(0);
        mainloop: for (int i = startIndex; i < total; i++) {
            if (Character.toLowerCase(src.charAt(i)) != c) {
                continue;
            }
            int j = 1;
            int k = i + 1;
            while (j < sublen) {
                char source = Character.toLowerCase(src.charAt(k));
                if (sub.charAt(j) != source) {
                    continue mainloop;
                }
                j++;
                k++;
            }
            return i;
        }
        return -1;
    }

    /**
     * 从右侧查找首次出现某字符串的位置（忽略大小写）.
     */
    public static int lastIndexOfIgnoreCase(String s, String subS) {
        return lastIndexOfIgnoreCase(s, subS, s.length(), 0);
    }

    /**
     * 从右侧查找首次出现某字符串的位置（忽略大小写）
     */
    public static int lastIndexOfIgnoreCase(String src, String subS, int startIndex) {
        return lastIndexOfIgnoreCase(src, subS, startIndex, 0);
    }

    /**
     * 从右侧查找首次出现某字符串的位置（忽略大小写）
     */
    public static int lastIndexOfIgnoreCase(String src, String sub, int startIndex, int endIndex) {
        int sublen = sub.length();
        int srclen = src.length();
        if (sublen == 0) {
            return startIndex > srclen ? srclen : (startIndex < -1 ? -1 : startIndex);
        }
        sub = sub.toLowerCase();
        int total = srclen - sublen;
        if (total < 0) {
            return -1;
        }
        if (startIndex >= total) {
            startIndex = total;
        }
        if (endIndex < 0) {
            endIndex = 0;
        }
        char c = sub.charAt(0);
        mainloop: for (int i = startIndex; i >= endIndex; i--) {
            if (Character.toLowerCase(src.charAt(i)) != c) {
                continue;
            }
            int j = 1;
            int k = i + 1;
            while (j < sublen) {
                char source = Character.toLowerCase(src.charAt(k));
                if (sub.charAt(j) != source) {
                    continue mainloop;
                }
                j++;
                k++;
            }
            return i;
        }
        return -1;
    }

    /**
     * 从右侧查找首次出现某字符串的位置
     */
    public static int lastIndexOf(String src, String sub, int startIndex, int endIndex) {
        int sublen = sub.length();
        int srclen = src.length();
        if (sublen == 0) {
            return startIndex > srclen ? srclen : (startIndex < -1 ? -1 : startIndex);
        }
        int total = srclen - sublen;
        if (total < 0) {
            return -1;
        }
        if (startIndex >= total) {
            startIndex = total;
        }
        if (endIndex < 0) {
            endIndex = 0;
        }
        char c = sub.charAt(0);
        mainloop: for (int i = startIndex; i >= endIndex; i--) {
            if (src.charAt(i) != c) {
                continue;
            }
            int j = 1;
            int k = i + 1;
            while (j < sublen) {
                if (sub.charAt(j) != src.charAt(k)) {
                    continue mainloop;
                }
                j++;
                k++;
            }
            return i;
        }
        return -1;
    }

    /**
     * 从右侧查找首次出现某字符的位置
     */
    public static int lastIndexOf(String src, char c, int startIndex, int endIndex) {
        int total = src.length() - 1;
        if (total < 0) {
            return -1;
        }
        if (startIndex >= total) {
            startIndex = total;
        }
        if (endIndex < 0) {
            endIndex = 0;
        }
        for (int i = startIndex; i >= endIndex; i--) {
            if (src.charAt(i) == c) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 从右侧查找首次出现某字符的位置
     */
    public static int lastIndexOfIgnoreCase(String src, char c, int startIndex, int endIndex) {
        int total = src.length() - 1;
        if (total < 0) {
            return -1;
        }
        if (startIndex >= total) {
            startIndex = total;
        }
        if (endIndex < 0) {
            endIndex = 0;
        }
        c = Character.toLowerCase(c);
        for (int i = startIndex; i >= endIndex; i--) {
            if (Character.toLowerCase(src.charAt(i)) == c) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 从右侧查找首次出现空格的位置
     */
    public static int lastIndexOfWhitespace(String src) {
        return lastIndexOfWhitespace(src, src.length(), 0);
    }

    /**
     * 从右侧查找首次出现空格的位置
     */
    public static int lastIndexOfWhitespace(String src, int startIndex) {
        return lastIndexOfWhitespace(src, startIndex, 0);
    }

    /**
     * 从右侧查找首次出现空格的位置
     */
    public static int lastIndexOfWhitespace(String src, int startIndex, int endIndex) {
        int total = src.length() - 1;
        if (total < 0) {
            return -1;
        }
        if (startIndex >= total) {
            startIndex = total;
        }
        if (endIndex < 0) {
            endIndex = 0;
        }
        for (int i = startIndex; i >= endIndex; i--) {
            if (Character.isWhitespace(src.charAt(i))) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 从右侧查找首次出现非空格的位置
     */
    public static int lastIndexOfNonWhitespace(String src) {
        return lastIndexOfNonWhitespace(src, src.length(), 0);
    }

    /**
     * 从右侧查找首次出现非空格的位置
     */
    public static int lastIndexOfNonWhitespace(String src, int startIndex) {
        return lastIndexOfNonWhitespace(src, startIndex, 0);
    }

    /**
     * 从右侧查找首次出现非空格的位置
     */
    public static int lastIndexOfNonWhitespace(String src, int startIndex, int endIndex) {
        int total = src.length() - 1;
        if (total < 0) {
            return -1;
        }
        if (startIndex >= total) {
            startIndex = total;
        }
        if (endIndex < 0) {
            endIndex = 0;
        }
        for (int i = startIndex; i >= endIndex; i--) {
            if (!Character.isWhitespace(src.charAt(i))) {
                return i;
            }
        }
        return -1;
    }

    // ---------------------------------------------------------------- starts and ends

    /**
     * 是否以指定的字符串开头（忽略大小写）
     */
    public static boolean startsWithIgnoreCase(String src, String subS) {
        return startsWithIgnoreCase(src, subS, 0);
    }

    /**
     * 是否以指定的字符串开头（忽略大小写）
     */
    public static boolean startsWithIgnoreCase(String src, String subS, int startIndex) {
        String sub = subS.toLowerCase();
        int sublen = sub.length();
        if (startIndex + sublen > src.length()) {
            return false;
        }
        int j = 0;
        int i = startIndex;
        while (j < sublen) {
            char source = Character.toLowerCase(src.charAt(i));
            if (sub.charAt(j) != source) {
                return false;
            }
            j++;
            i++;
        }
        return true;
    }

    /**
     * 是否以指定的字符串结尾（忽略大小写）
     */
    public static boolean endsWithIgnoreCase(String src, String subS) {
        String sub = subS.toLowerCase();
        int sublen = sub.length();
        int j = 0;
        int i = src.length() - sublen;
        if (i < 0) {
            return false;
        }
        while (j < sublen) {
            char source = Character.toLowerCase(src.charAt(i));
            if (sub.charAt(j) != source) {
                return false;
            }
            j++;
            i++;
        }
        return true;
    }

    /**
     * 是否以指定的字符开头
     */
    public static boolean startsWithChar(String s, char c) {
        return s != null && s.length() != 0 && s.charAt(0) == c;
    }

    /**
     * 是否以指定的字符结尾
     */
    public static boolean endsWithChar(String s, char c) {
        return s != null && s.length() != 0 && s.charAt(s.length() - 1) == c;
    }

    // ---------------------------------------------------------------- count substrings

    /**
     * Counts substring occurrences in a source string.
     *
     * @param source source string
     * @param sub substring to count
     * @return number of substring occurrences
     */
    public static int count(String source, String sub) {
        return count(source, sub, 0);
    }

    public static int count(String source, String sub, int start) {
        int count = 0;
        int j = start;
        int sublen = sub.length();
        if (sublen == 0) {
            return 0;
        }
        while (true) {
            int i = source.indexOf(sub, j);
            if (i == -1) {
                break;
            }
            count++;
            j = i + sublen;
        }
        return count;
    }

    public static int count(String source, char c) {
        return count(source, c, 0);
    }

    public static int count(String source, char c, int start) {
        int count = 0;
        int j = start;
        while (true) {
            int i = source.indexOf(c, j);
            if (i == -1) {
                break;
            }
            count++;
            j = i + 1;
        }
        return count;
    }

    /**
     * Count substring occurrences in a source string, ignoring case.
     *
     * @param source source string
     * @param sub substring to count
     * @return number of substring occurrences
     */
    public static int countIgnoreCase(String source, String sub) {
        int count = 0;
        int j = 0;
        int sublen = sub.length();
        if (sublen == 0) {
            return 0;
        }
        while (true) {
            int i = indexOfIgnoreCase(source, sub, j);
            if (i == -1) {
                break;
            }
            count++;
            j = i + sublen;
        }
        return count;
    }

    // ---------------------------------------------------------------- string arrays

    /**
     * Finds the very first index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     */
    public static int[] indexOf(String s, String[] arr) {
        return indexOf(s, arr, 0);
    }

    /**
     * Finds the very first index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     * @param start starting position
     */
    public static int[] indexOf(String s, String[] arr, int start) {
        int arrLen = arr.length;
        int index = Integer.MAX_VALUE;
        int last = -1;
        for (int j = 0; j < arrLen; j++) {
            int i = s.indexOf(arr[j], start);
            if (i != -1) {
                if (i < index) {
                    index = i;
                    last = j;
                }
            }
        }
        return last == -1 ? null : new int[] { last, index };
    }

    /**
     * Finds the very first index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     */
    public static int[] indexOfIgnoreCase(String s, String[] arr) {
        return indexOfIgnoreCase(s, arr, 0);
    }

    /**
     * Finds the very first index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     * @param start starting position
     */
    public static int[] indexOfIgnoreCase(String s, String[] arr, int start) {
        int arrLen = arr.length;
        int index = Integer.MAX_VALUE;
        int last = -1;
        for (int j = 0; j < arrLen; j++) {
            int i = indexOfIgnoreCase(s, arr[j], start);
            if (i != -1) {
                if (i < index) {
                    index = i;
                    last = j;
                }
            }
        }
        return last == -1 ? null : new int[] { last, index };
    }

    /**
     * Finds the very last index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     */
    public static int[] lastIndexOf(String s, String[] arr) {
        return lastIndexOf(s, arr, s.length());
    }

    /**
     * Finds the very last index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     * @param fromIndex starting position
     */
    public static int[] lastIndexOf(String s, String[] arr, int fromIndex) {
        int arrLen = arr.length;
        int index = -1;
        int last = -1;
        for (int j = 0; j < arrLen; j++) {
            int i = s.lastIndexOf(arr[j], fromIndex);
            if ((i != -1) && (i > index)) {
                index = i;
                last = j;
            }
        }
        return last == -1 ? null : new int[] { last, index };
    }

    /**
     * Finds the very last index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     * @return int[2]
     */
    public static int[] lastIndexOfIgnoreCase(String s, String[] arr) {
        return lastIndexOfIgnoreCase(s, arr, s.length());
    }

    /**
     * Finds the very last index of a substring from the specified array. It returns an int[2] where int[0] represents
     * the substring index and int[1] represents position where substring was found. Returns <code>null</code> if noting
     * found.
     *
     * @param s source string
     * @param arr string array
     * @param fromIndex starting position
     */
    public static int[] lastIndexOfIgnoreCase(String s, String[] arr, int fromIndex) {
        int arrLen = arr.length;
        int index = -1;
        int last = -1;
        for (int j = 0; j < arrLen; j++) {
            int i = lastIndexOfIgnoreCase(s, arr[j], fromIndex);
            if ((i != -1) && (i > index)) {
                index = i;
                last = j;
            }
        }
        return last == -1 ? null : new int[] { last, index };
    }

    /**
     * Compares two string arrays.
     *
     * @param as first string array
     * @param as1 second string array
     * @return <code>true</code> if all array elements matches
     */
    public static boolean equals(String[] as, String[] as1) {
        if (as.length != as1.length) {
            return false;
        }
        for (int i = 0; i < as.length; i++) {
            if (!as[i].equals(as1[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Compares two string arrays.
     *
     * @param as first string array
     * @param as1 second string array
     * @return true if all array elements matches
     */
    public static boolean equalsIgnoreCase(String[] as, String[] as1) {
        if (as.length != as1.length) {
            return false;
        }
        for (int i = 0; i < as.length; i++) {
            if (!as[i].equalsIgnoreCase(as1[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Replaces many substring at once. Order of string array is important.
     *
     * @param s source string
     * @param sub substrings array
     * @param with replace with array
     * @return string with all occurrences of substrings replaced
     */
    public static String replace(String s, String[] sub, String[] with) {
        if ((sub.length != with.length) || (sub.length == 0)) {
            return s;
        }
        int start = 0;
        StringBuilder buf = new StringBuilder(s.length());
        while (true) {
            int[] res = indexOf(s, sub, start);
            if (res == null) {
                break;
            }
            int end = res[1];
            buf.append(s.substring(start, end));
            buf.append(with[res[0]]);
            start = end + sub[res[0]].length();
        }
        buf.append(s.substring(start));
        return buf.toString();
    }

    /**
     * Replaces many substring at once. Order of string array is important.
     *
     * @param s source string
     * @param sub substrings array
     * @param with replace with array
     * @return string with all occurrences of substrings replaced
     */
    public static String replaceIgnoreCase(String s, String[] sub, String[] with) {
        if ((sub.length != with.length) || (sub.length == 0)) {
            return s;
        }
        int start = 0;
        StringBuilder buf = new StringBuilder(s.length());
        while (true) {
            int[] res = indexOfIgnoreCase(s, sub, start);
            if (res == null) {
                break;
            }
            int end = res[1];
            buf.append(s.substring(start, end));
            buf.append(with[res[0]]);
            start = end + sub[0].length();
        }
        buf.append(s.substring(start));
        return buf.toString();
    }

    /**
     * 比较两个字符串（大小写敏感）。
     *
     * <pre>
     * equals(null, null)   = true
     * equals(null, &quot;abc&quot;)  = false
     * equals(&quot;abc&quot;, null)  = false
     * equals(&quot;abc&quot;, &quot;abc&quot;) = true
     * equals(&quot;abc&quot;, &quot;ABC&quot;) = false
     * </pre>
     *
     * @param str1 要比较的字符串1
     * @param str2 要比较的字符串2
     * @return 如果两个字符串相同，或者都是<code>null</code>，则返回<code>true</code>
     */
    public static boolean equals(String str1, String str2) {
        if (str1 == null) {
            return str2 == null;
        }

        return str1.equals(str2);
    }

    /**
     * 比较两个字符串（大小写不敏感）。
     *
     * <pre>
     * equalsIgnoreCase(null, null)   = true
     * equalsIgnoreCase(null, &quot;abc&quot;)  = false
     * equalsIgnoreCase(&quot;abc&quot;, null)  = false
     * equalsIgnoreCase(&quot;abc&quot;, &quot;abc&quot;) = true
     * equalsIgnoreCase(&quot;abc&quot;, &quot;ABC&quot;) = true
     * </pre>
     *
     * @param str1 要比较的字符串1
     * @param str2 要比较的字符串2
     * @return 如果两个字符串相同，或者都是<code>null</code>，则返回<code>true</code>
     */
    public static boolean equalsIgnoreCase(String str1, String str2) {
        if (str1 == null) {
            return str2 == null;
        }

        return str1.equalsIgnoreCase(str2);
    }

    /**
     * Compares string with at least one from the provided array. If at least one equal string is found, returns its
     * index. Otherwise, <code>-1</code> is returned.
     */
    public static int equalsOne(String src, String[] dest) {
        for (int i = 0; i < dest.length; i++) {
            if (src.equals(dest[i])) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Compares string with at least one from the provided array, ignoring case. If at least one equal string is found,
     * it returns its index. Otherwise, <code>-1</code> is returned.
     */
    public static int equalsOneIgnoreCase(String src, String[] dest) {
        for (int i = 0; i < dest.length; i++) {
            if (src.equalsIgnoreCase(dest[i])) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Checks if string starts with at least one string from the provided array. If at least one string is matched, it
     * returns its index. Otherwise, <code>-1</code> is returned.
     */
    public static int startsWithOne(String src, String[] dest) {
        for (int i = 0; i < dest.length; i++) {
            String m = dest[i];
            if (m == null) {
                continue;
            }
            if (src.startsWith(m)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Checks if string starts with at least one string from the provided array. If at least one string is matched, it
     * returns its index. Otherwise, <code>-1</code> is returned.
     */
    public static int startsWithOneIgnoreCase(String src, String[] dest) {
        for (int i = 0; i < dest.length; i++) {
            String m = dest[i];
            if (m == null) {
                continue;
            }
            if (startsWithIgnoreCase(src, m)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 是否以任意一个字符串结尾
     */
    public static int endsWithOne(String src, String[] dest) {
        for (int i = 0; i < dest.length; i++) {
            String m = dest[i];
            if (m == null) {
                continue;
            }
            if (src.endsWith(m)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 是否以任意一个字符串结尾
     */
    public static int endsWithOneIgnoreCase(String src, String[] dest) {
        for (int i = 0; i < dest.length; i++) {
            String m = dest[i];
            if (m == null) {
                continue;
            }
            if (endsWithIgnoreCase(src, m)) {
                return i;
            }
        }
        return -1;
    }

    // ---------------------------------------------------------------- char based

    /**
     * @see #indexOfChars(String, String, int)
     */
    public static int indexOfChars(String string, String chars) {
        return indexOfChars(string, chars, 0);
    }

    /**
     * Returns the very first index of any char from provided string, starting from specified index offset. Returns
     * index of founded char, or <code>-1</code> if nothing found.
     */
    public static int indexOfChars(String string, String chars, int startIndex) {
        int stringLen = string.length();
        int charsLen = chars.length();
        int start = startIndex;
        if (start < 0) {
            start = 0;
        }
        for (int i = start; i < stringLen; i++) {
            char c = string.charAt(i);
            for (int j = 0; j < charsLen; j++) {
                if (c == chars.charAt(j)) {
                    return i;
                }
            }
        }
        return -1;
    }

    public static int indexOfChars(String string, char[] chars) {
        return indexOfChars(string, chars, 0);
    }

    /**
     * Returns the very first index of any char from provided string, starting from specified index offset. Returns
     * index of founded char, or <code>-1</code> if nothing found.
     */
    public static int indexOfChars(String string, char[] chars, int startindex) {
        int stringLen = string.length();
        for (int i = startindex; i < stringLen; i++) {
            char c = string.charAt(i);
            for (char aChar : chars) {
                if (c == aChar) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * Returns first index of a whitespace character.
     */
    public static int indexOfWhitespace(String string) {
        return indexOfWhitespace(string, 0, string.length());
    }

    public static int indexOfWhitespace(String string, int startindex) {
        return indexOfWhitespace(string, startindex, string.length());
    }

    /**
     * Returns first index of a whitespace character, starting from specified index offset.
     */
    public static int indexOfWhitespace(String string, int startindex, int endindex) {
        for (int i = startindex; i < endindex; i++) {
            if (CharUtil.isWhitespace(string.charAt(i))) {
                return i;
            }
        }
        return -1;
    }

    public static int indexOfNonWhitespace(String string) {
        return indexOfNonWhitespace(string, 0, string.length());
    }

    public static int indexOfNonWhitespace(String string, int startindex) {
        return indexOfNonWhitespace(string, startindex, string.length());
    }

    public static int indexOfNonWhitespace(String string, int startindex, int endindex) {
        for (int i = startindex; i < endindex; i++) {
            if (!CharUtil.isWhitespace(string.charAt(i))) {
                return i;
            }
        }
        return -1;
    }

    // ---------------------------------------------------------------- strip, trim

    /**
     * Strips leading char if string starts with one.
     */
    public static String stripLeadingChar(String string, char c) {
        if (string.length() > 0) {
            if (string.charAt(0) == c) {
                return string.substring(1);
            }
        }
        return string;
    }

    /**
     * Strips trailing char if string ends with one.
     */
    public static String stripTrailingChar(String string, char c) {
        if ((string.length() > 0) && (string.charAt(string.length() - 1) == c)) {
            return string.substring(0, string.length() - 1);
        }
        return string;
    }

    /**
     * Strips leading and trailing char from given string.
     */
    public static String stripChar(String string, char c) {
        if (string.length() == 0) {
            return string;
        }
        if (string.length() == 1) {
            if (string.charAt(0) == c) {
                return EMPTY;
            }
            return string;
        }
        int left = 0;
        int right = string.length();

        if (string.charAt(left) == c) {
            left++;
        }
        if (string.charAt(right - 1) == c) {
            right--;
        }
        return string.substring(left, right);
    }

    /**
     * 剥离掉指定字符出现之前的字符串. 包含该字符.
     */
    public static String stripToChar(String string, char c) {
        int ndx = string.indexOf(c);

        if (ndx == -1) {
            return string;
        }

        return string.substring(ndx);
    }

    /**
     * 剥离掉指定字符出现之后的字符串. 不包含该字符.
     */
    public static String stripFromChar(String string, char c) {
        int ndx = string.indexOf(c);

        if (ndx == -1) {
            return string;
        }

        return string.substring(0, ndx);
    }

    /**
     * 去除字符串两端的空白字符，如果为null返回null
     *
     * @param str 字符串
     * @return 处理后的字符串
     */
    public static String trim(String str) {
        return (null == str) ? null : str.trim();
    }

    public static String trimToNull(String str) {
        String ts = trim(str);
        return isEmpty(ts) ? null : ts;
    }

    /**
     * 去掉数组中每一个字符串两端的空白字符. 忽略为 <code>null</code> 的元素.
     */
    public static void trimAll(String[] strings) {
        for (int i = 0; i < strings.length; i++) {
            String string = strings[i];
            if (string != null) {
                strings[i] = trim(string);
            }
        }
    }

    /**
     * 去掉数组中的字符串两端的空白字符
     *
     * @see #trimDown(String)
     */
    public static void trimDownAll(String[] strings) {
        for (int i = 0; i < strings.length; i++) {
            String string = strings[i];
            if (string != null) {
                strings[i] = trimDown(string);
            }
        }
    }

    /**
     * 去掉字符串两端的空白字符，如果处理后的字符串长度为 0, 时返回 <code>null</code>.
     */
    public static String trimDown(String string) {
        string = trim(string);
        if (string.length() == 0) {
            string = null;
        }
        return string;
    }

    /**
     * Crops string by setting empty strings to <code>null</code>.
     */
    public static String crop(String string) {
        if (string.length() == 0) {
            return null;
        }
        return string;
    }

    /**
     * Crops all elements of string array.
     */
    public static void cropAll(String[] strings) {
        for (int i = 0; i < strings.length; i++) {
            String string = strings[i];
            if (string != null) {
                string = crop(strings[i]);
            }
            strings[i] = string;
        }
    }

    /**
     * 去掉字符串左端的空白字符.
     */
    public static String trimLeft(String src) {
        int len = src.length();
        int st = 0;
        while ((st < len) && (CharUtil.isWhitespace(src.charAt(st)))) {
            st++;
        }
        return st > 0 ? src.substring(st) : src;
    }

    /**
     * 去掉字符串右端的空白字符.
     */
    public static String trimRight(String src) {
        int len = src.length();
        int count = len;
        while ((len > 0) && (CharUtil.isWhitespace(src.charAt(len - 1)))) {
            len--;
        }
        return (len < count) ? src.substring(0, len) : src;
    }

    // ---------------------------------------------------------------- regions

    /**
     *
     * @see #indexOfRegion(String, String, String, int)
     */
    public static int[] indexOfRegion(String string, String leftBoundary, String rightBoundary) {
        return indexOfRegion(string, leftBoundary, rightBoundary, 0);
    }

    /**
     * 获取两个边界字符串出现的范围.
     *
     * @see #indexOfRegion(String, String, String, char, int)
     */
    public static int[] indexOfRegion(String string, String leftBoundary, String rightBoundary, int offset) {
        int ndx = offset;
        int[] res = new int[4];
        ndx = string.indexOf(leftBoundary, ndx);
        if (ndx == -1) {
            return null;
        }
        res[0] = ndx;
        ndx += leftBoundary.length();
        res[1] = ndx;

        ndx = string.indexOf(rightBoundary, ndx);
        if (ndx == -1) {
            return null;
        }
        res[2] = ndx;
        res[3] = ndx + rightBoundary.length();
        return res;
    }

    /**
     * 获取两个边界字符串出现的范围.
     *
     * @see #indexOfRegion(String, String, String, char, int)
     */
    public static int[] indexOfRegion(String string, String leftBoundary, String rightBoundary, char escape) {
        return indexOfRegion(string, leftBoundary, rightBoundary, escape, 0);
    }

    /**
     * 获取两个边界字符串出现的范围.
     */
    public static int[] indexOfRegion(String string, String leftBoundary, String rightBoundary, char escape, int offset) {
        int ndx = offset;
        int[] res = new int[4];
        while (true) {
            ndx = string.indexOf(leftBoundary, ndx);
            if (ndx == -1) {
                return null;
            }
            int leftBoundaryLen = leftBoundary.length();
            if (ndx > 0) {
                if (string.charAt(ndx - 1) == escape) { // check previous char
                    boolean cont = true;
                    if (ndx > 1) {
                        if (string.charAt(ndx - 2) == escape) { // check double escapes
                            ndx--;
                            leftBoundaryLen++;
                            cont = false;
                        }
                    }
                    if (cont) {
                        ndx += leftBoundaryLen;
                        continue;
                    }
                }
            }
            res[0] = ndx;
            ndx += leftBoundaryLen;
            res[1] = ndx;

            while (true) { // find right boundary
                ndx = string.indexOf(rightBoundary, ndx);
                if (ndx == -1) {
                    return null;
                }
                if (ndx > 0) {
                    if (string.charAt(ndx - 1) == escape) {
                        ndx += rightBoundary.length();
                        continue;
                    }
                }
                res[2] = ndx;
                res[3] = ndx + rightBoundary.length();
                return res;
            }
        }
    }

    // ---------------------------------------------------------------- join

    /**
     * 连接数组的所有元素为字符串
     */
    public static String join(Object... array) {
        if (array == null) {
            return null;
        }

        if (array.length == 0) {
            return EMPTY;
        }

        if (array.length == 1) {
            return String.valueOf(array[0]);
        }

        final StringBuilder sb = new StringBuilder(array.length * 16);

        for (Object anArray : array) {
            sb.append(anArray);
        }

        return sb.toString();
    }

    /**
     * 用指定的分隔符连接数组的所有元素
     */
    public static String join(Object[] array, char separator) {
        if (array == null) {
            return null;
        }

        if (array.length == 0) {
            return EMPTY;
        }

        if (array.length == 1) {
            return String.valueOf(array[0]);
        }

        final StringBuilder sb = new StringBuilder(array.length * 16);

        for (int i = 0; i < array.length; i++) {

            if (i > 0) {
                sb.append(separator);
            }

            sb.append(array[i]);
        }

        return sb.toString();
    }

    /**
     * 用指定的分隔符连接数组的所有元素
     */
    public static String join(Object[] array, String separator) {
        if (array == null) {
            return null;
        }

        if (array.length == 0) {
            return EMPTY;
        }

        if (array.length == 1) {
            return String.valueOf(array[0]);
        }

        final StringBuilder sb = new StringBuilder(array.length * 16);

        for (int i = 0; i < array.length; i++) {

            if (i > 0) {
                sb.append(separator);
            }

            sb.append(array[i]);
        }

        return sb.toString();
    }

    // ---------------------------------------------------------------- charset

    /**
     * 更改字符串的字符集.
     */
    public static String convertCharset(String source, String srcCharsetName, String newCharsetName) {
        if (srcCharsetName.equals(newCharsetName)) {
            return source;
        }
        try {
            return new String(source.getBytes(srcCharsetName), newCharsetName);
        } catch (UnsupportedEncodingException unex) {
            throw new IllegalArgumentException(unex);
        }
    }

    /**
     * Java 特殊字符转义.
     */
    public static String escapeJava(String string) {
        int strLen = string.length();
        StringBuilder sb = new StringBuilder(strLen);

        for (int i = 0; i < strLen; i++) {
            char c = string.charAt(i);
            switch (c) {
                case '\b':
                    sb.append("\\b");
                    break;
                case '\t':
                    sb.append("\\t");
                    break;
                case '\n':
                    sb.append("\\n");
                    break;
                case '\f':
                    sb.append("\\f");
                    break;
                case '\r':
                    sb.append("\\r");
                    break;
                case '\"':
                    sb.append("\\\"");
                    break;
                case '\\':
                    sb.append("\\\\");
                    break;
                default:
                    if ((c < 32) || (c > 127)) {
                        String hex = Integer.toHexString(c);
                        sb.append("\\u");
                        for (int k = hex.length(); k < 4; k++) {
                            sb.append('0');
                        }
                        sb.append(hex);
                    } else {
                        sb.append(c);
                    }
            }
        }
        return sb.toString();
    }

    /**
     * Java 特殊字符反转义.
     */
    public static String unescapeJava(String str) {
        char[] chars = str.toCharArray();

        StringBuilder sb = new StringBuilder(str.length());
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            if (c != '\\') {
                sb.append(c);
                continue;
            }
            i++;
            c = chars[i];
            switch (c) {
                case 'b':
                    sb.append('\b');
                    break;
                case 't':
                    sb.append('\t');
                    break;
                case 'n':
                    sb.append('\n');
                    break;
                case 'f':
                    sb.append('\f');
                    break;
                case 'r':
                    sb.append('\r');
                    break;
                case '"':
                    sb.append('\"');
                    break;
                case '\\':
                    sb.append('\\');
                    break;
                case 'u':
                    char hex = (char) Integer.parseInt(new String(chars, i + 1, 4), 16);
                    sb.append(hex);
                    i += 4;
                    break;
                default:
                    throw new IllegalArgumentException("Invalid escaping character: " + c);
            }
        }
        return sb.toString();
    }

    // ---------------------------------------------------------------- chars

    /**
     * 比较字符串的指定位置是否是某个字符
     */
    public static boolean isCharAtEqual(String string, int index, char charToCompare) {
        if ((index < 0) || (index >= string.length())) {
            return false;
        }
        return string.charAt(index) == charToCompare;
    }

    // ---------------------------------------------------------------- surround

    /**
     * 用固定的字符串包裹指定字符串的两端
     *
     * @see #surround(String, String, String)
     */
    public static String surround(String string, String fix) {
        return surround(string, fix, fix);
    }

    /**
     * 用指定前缀和后缀包裹一个字符串（如果已经有就忽略）
     */
    public static String surround(String string, String prefix, String suffix) {
        if (!string.startsWith(prefix)) {
            string = prefix + string;
        }
        if (!string.endsWith(suffix)) {
            string += suffix;
        }
        return string;
    }

    /**
     * 插入指定的前缀， 当字符串已经以该前缀开头时原样返回.
     */
    public static String prefix(String string, String prefix) {
        if (!string.startsWith(prefix)) {
            string = prefix + string;
        }
        return string;
    }

    /**
     * 追加指定的后缀， 当字符串已经以该后缀结尾时原样返回.
     */
    public static String suffix(String string, String suffix) {
        if (!string.endsWith(suffix)) {
            string += suffix;
        }
        return string;
    }

    /**
     * 包装指定字符串
     *
     * @param str 被包装的字符串
     * @param prefix 前缀
     * @param suffix 后缀
     * @return 包装后的字符串
     */
    public static String wrap(String str, String prefix, String suffix) {
        return format("{}{}{}", prefix, str, suffix);
    }

    // ---------------------------------------------------------------- cut

    /**
     * 切割字符串，从开头切割到指定的子串出现的位置.
     */
    public static String cutToIndexOf(String string, String substring) {
        int i = string.indexOf(substring);
        if (i != -1) {
            string = string.substring(0, i);
        }
        return string;
    }

    /**
     * 切割字符串，从开头切割到指定的字符出现的位置.
     */
    public static String cutToIndexOf(String string, char c) {
        int i = string.indexOf(c);
        if (i != -1) {
            string = string.substring(0, i);
        }
        return string;
    }

    /**
     * 切割字符串，从指定的子串出现的位置切割到结束.
     */
    public static String cutFromIndexOf(String string, String substring) {
        int i = string.indexOf(substring);
        if (i != -1) {
            string = string.substring(i);
        }
        return string;
    }

    /**
     * 切割字符串，从指定的字符出现的位置切割到结束.
     */
    public static String cutFromIndexOf(String string, char c) {
        int i = string.indexOf(c);
        if (i != -1) {
            string = string.substring(i);
        }
        return string;
    }

    /**
     * 去掉指定前缀.
     */
    public static String removePrefix(String string, String prefix) {
        if(isEmpty(string) || isEmpty(prefix)){
            return string;
        }
        if (string.startsWith(prefix)) {
            string = string.substring(prefix.length());
        }
        return string;
    }

    /**
     * 忽略大小写去掉指定前缀
     */
    public static String removePrefixIgnoreCase(String str, String prefix) {
        if(isEmpty(str) || isEmpty(prefix)){
            return str;
        }

        if (str.toLowerCase().startsWith(prefix.toLowerCase())) {
            return str.substring(prefix.length());
        }
        return str;
    }

    /**
     * 去掉指定后缀.
     */
    public static String removeSuffix(String string, String suffix) {
        if(isEmpty(string) || isEmpty(suffix)){
            return string;
        }

        if (string.endsWith(suffix)) {
            string = string.substring(0, string.length() - suffix.length());
        }
        return string;
    }

    /**
     * 忽略大小写去掉指定后缀
     */
    public static String removeSuffixIgnoreCase(String str, String suffix) {
        if(isEmpty(str) || isEmpty(suffix)){
            return str;
        }

        if (str.toLowerCase().endsWith(suffix.toLowerCase())) {
            return str.substring(0, str.length() - suffix.length());
        }
        return str;
    }

    /**
     * 移除前缀和后缀.
     *
     * @see #removeSurrounding(String, String, String)
     */
    public static String removeSurrounding(String string, String fix) {
        return removeSurrounding(string, fix, fix);
    }

    /**
     * 移除前缀和后缀.
     */
    public static String removeSurrounding(String string, String prefix, String suffix) {
        int start = 0;
        int end = string.length();
        if (string.startsWith(prefix)) {
            start = prefix.length();
        }
        if (string.endsWith(suffix)) {
            end -= suffix.length();
        }

        return string.substring(start, end);
    }

    /**
     * 切割出给定的两个字符串中间的子串. <br/>
     * 如果 left 或 right 中任何一个都未出现, 会返回 <code>null</code>. <br/>
     * 如果出现位置颠倒, 会返回空字符串.
     */
    public static String cutBetween(String string, String left, String right) {
        int leftNdx = string.indexOf(left);
        if (leftNdx == -1) {
            return null;
        }

        int rightNdx = string.indexOf(right);
        if (rightNdx == -1) {
            return null;
        }

        leftNdx += left.length();

        if (leftNdx >= rightNdx) {
            return EMPTY;
        }

        return string.substring(leftNdx, rightNdx);
    }

    // ---------------------------------------------------------------- escaped

    /**
     * Returns <code>true</code> if character at provided index position is escaped by escape character.
     */
    public static boolean isCharAtEscaped(String src, int ndx, char escapeChar) {
        if (ndx == 0) {
            return false;
        }
        ndx--;
        return src.charAt(ndx) == escapeChar;
    }

    public static int indexOfUnescapedChar(String src, char sub, char escapeChar) {
        return indexOfUnescapedChar(src, sub, escapeChar, 0);
    }

    public static int indexOfUnescapedChar(String src, char sub, char escapeChar, int startIndex) {
        if (startIndex < 0) {
            startIndex = 0;
        }
        int srclen = src.length();
        char previous;
        char c = 0;
        for (int i = startIndex; i < srclen; i++) {
            previous = c;
            c = src.charAt(i);
            if (c == sub) {
                if (i > startIndex) {
                    if (previous == escapeChar) {
                        continue;
                    }
                }
                return i;
            }
        }
        return -1;

    }

    // ---------------------------------------------------------------- insert

    public static String insert(String src, String insert) {
        return insert(src, insert, 0);
    }

    /**
     * 插入一个子串到字符串的指定偏移位置.
     */
    public static String insert(String src, String insert, int offset) {
        if (offset < 0) {
            offset = 0;
        }
        if (offset > src.length()) {
            offset = src.length();
        }
        StringBuilder sb = new StringBuilder(src);
        sb.insert(offset, insert);
        return sb.toString();
    }

    // ---------------------------------------------------------------- misc

    /**
     * 重复某一段字符串.
     */
    public static String repeat(String source, int count) {
        StringBuilder result = new StringBuilder(count);
        while (count > 0) {
            result.append(source);
            count--;
        }
        return result.toString();
    }

    /**
     * 重复某一字符得到的字符串.
     */
    public static String repeat(char c, int count) {
        char[] result = new char[count];
        for (int i = 0; i < count; i++) {
            result[i] = c;
        }
        return new String(result);
    }

    /**
     * 反转字符串
     */
    public static String reverse(String s) {
        StringBuilder result = new StringBuilder(s.length());
        for (int i = s.length() - 1; i >= 0; i--) {
            result.append(s.charAt(i));
        }
        return result.toString();
    }

    /**
     * 获取两个字符串的最大共有前缀
     */
    public static String maxCommonPrefix(String one, String two) {
        final int minLength = Math.min(one.length(), two.length());

        final StringBuilder sb = new StringBuilder(minLength);
        for (int pos = 0; pos < minLength; pos++) {
            final char currentChar = one.charAt(pos);
            if (currentChar != two.charAt(pos)) {
                break;
            }
            sb.append(currentChar);
        }

        return sb.toString();
    }

    // ---------------------------------------------------------------- camel case

    /**
     * 驼峰格式的字符串转换为按某一个分隔符连接的字符串
     *
     * @param input
     * @param separator
     * @return
     */
    public static String fromCamelCase(String input, char separator) {
        int length = input.length();
        StringBuilder result = new StringBuilder(length * 2);
        int resultLength = 0;
        boolean prevTranslated = false;
        for (int i = 0; i < length; i++) {
            char c = input.charAt(i);
            if (i > 0 || c != separator) {// skip first starting separator
                if (Character.isUpperCase(c)) {
                    if (!prevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != separator) {
                        result.append(separator);
                        resultLength++;
                    }
                    c = Character.toLowerCase(c);
                    prevTranslated = true;
                } else {
                    prevTranslated = false;
                }
                result.append(c);
                resultLength++;
            }
        }
        return resultLength > 0 ? result.toString() : input;
    }

    /**
     * 转按某一个分隔符连接的字符串换为驼峰格式的字符串.
     */
    public static String toCamelCase(String input, boolean firstCharUppercase, char separator) {
        int length = input.length();
        StringBuilder sb = new StringBuilder(length);
        boolean upperCase = firstCharUppercase;

        for (int i = 0; i < length; i++) {
            char ch = input.charAt(i);
            if (ch == separator) {
                upperCase = true;
            } else if (upperCase) {
                sb.append(Character.toUpperCase(ch));
                upperCase = false;
            } else {
                sb.append(ch);
            }
        }
        return sb.toString();
    }

    // ---------------------------------------------------------------- prefixes

    /**
     * 在多个字符串中找出共同的前缀，如果没有就返回空字符串
     */
    public static String findCommonPrefix(String... strings) {
        StringBuilder prefix = new StringBuilder();
        int index = 0;
        char c = 0;

        loop: while (true) {
            for (int i = 0; i < strings.length; i++) {

                String s = strings[i];
                if (index == s.length()) {
                    break loop;
                }

                if (i == 0) {
                    c = s.charAt(index);
                } else {
                    if (s.charAt(index) != c) {
                        break loop;
                    }
                }
            }

            index++;
            prefix.append(c);
        }
        return prefix.length() == 0 ? EMPTY : prefix.toString();
    }

    // ---------------------------------------------------------------- shorten

    /**
     * Shorten string to given length.
     */
    public static String shorten(String s, int length, String suffix) {
        length -= suffix.length();

        if (s.length() > length) {
            for (int j = length; j >= 0; j--) {
                if (CharUtil.isWhitespace(s.charAt(j))) {
                    length = j;
                    break;
                }
            }
            String temp = s.substring(0, length);
            s = temp.concat(suffix);
        }

        return s;
    }

    // ---------------------------------------------------------------- text

    public static String escape(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // These characters are part of the query syntax and must be escaped
            if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
                || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
                || c == '*' || c == '?' || c == '|' || c == '&' || c == '/') {
                sb.append('\\');
            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * Formats provided string as paragraph.
     */
    public static String formatParagraph(String src, int len, boolean breakOnWhitespace) {
        StringBuilder str = new StringBuilder();
        int total = src.length();
        int from = 0;
        while (from < total) {
            int to = from + len;
            if (to >= total) {
                to = total;
            } else if (breakOnWhitespace) {
                int ndx = lastIndexOfWhitespace(src, to - 1, from);
                if (ndx != -1) {
                    to = ndx + 1;
                }
            }
            int cutFrom = indexOfNonWhitespace(src, from, to);
            if (cutFrom != -1) {
                int cutTo = lastIndexOfNonWhitespace(src, to - 1, from) + 1;
                str.append(src.substring(cutFrom, cutTo));
            }
            str.append('\n');
            from = to;
        }
        return str.toString();
    }

    /**
     * Converts all tabs on a line to spaces according to the provided tab width. This is not a simple tab to spaces
     * replacement, since the resulting indentation remains the same.
     */
    public static String convertTabsToSpaces(String line, int tabWidth) {
        int tabIndex, tabSize;
        int lastTabIndex = 0;
        int addedChars = 0;

        if (tabWidth == 0) {
            return remove(line, '\t');
        }

        StringBuilder result = new StringBuilder();

        while ((tabIndex = line.indexOf('\t', lastTabIndex)) != -1) {
            tabSize = tabWidth - ((tabIndex + addedChars) % tabWidth);
            if (tabSize == 0) {
                tabSize = tabWidth;
            }
            addedChars += tabSize - 1;
            result.append(line.substring(lastTabIndex, tabIndex));
            result.append(repeat(' ', tabSize));
            lastTabIndex = tabIndex + 1;
        }

        if (lastTabIndex == 0) {
            return line;
        }

        result.append(line.substring(lastTabIndex));
        return result.toString();
    }

    // ---------------------------------------------------------------- case change

    /**
     * 转为小写
     */
    public static String toLowerCase(String s) {
        return toLowerCase(s, null);
    }

    /**
     * 转为小写
     */
    public static String toLowerCase(String s, Locale locale) {
        if (s == null) {
            return null;
        }

        StringBuilder sb = null;

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            if (c > 127) {
                // found non-ascii char, fallback to the slow unicode detection

                if (locale == null) {
                    locale = Locale.getDefault();
                }

                return s.toLowerCase(locale);
            }

            if ((c >= 'A') && (c <= 'Z')) {
                if (sb == null) {
                    sb = new StringBuilder(s);
                }

                sb.setCharAt(i, (char) (c + 32));
            }
        }

        if (sb == null) {
            return s;
        }

        return sb.toString();
    }

    /**
     * 转为大写
     */
    public static String toUpperCase(String s) {
        return toUpperCase(s, null);
    }

    /**
     * 转为大写
     */
    public static String toUpperCase(String s, Locale locale) {
        if (s == null) {
            return null;
        }
        StringBuilder sb = null;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c > 127) {
                // found non-ascii char, fallback to the slow unicode detection
                if (locale == null) {
                    locale = Locale.getDefault();
                }
                return s.toUpperCase(locale);
            }
            if ((c >= 'a') && (c <= 'z')) {
                if (sb == null) {
                    sb = new StringBuilder(s);
                }
                sb.setCharAt(i, (char) (c - 32));
            }
        }
        if (sb == null) {
            return s;
        }
        return sb.toString();
    }

    /**
     * 编码字符串
     */
    public static byte[] encode(String str, String charset) {
        if (str == null) {
            return null;
        }

        if (isBlank(charset)) {
            return str.getBytes();
        }
        try {
            return str.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(format("Charset [{}] unsupported!", charset), e);
        }
    }

    /**
     * 补足字符串以满足最小长度 StringUtil.padPre("1", 3, '0');//"001"
     *
     * @param str 字符串
     * @param minLength 最小长度
     * @param padChar 补充的字符
     * @return 补充后的字符串
     */
    public static String padPre(String str, int minLength, char padChar) {
        if (str.length() >= minLength) {
            return str;
        }
        StringBuilder sb = new StringBuilder(minLength);
        for (int i = str.length(); i < minLength; i++) {
            sb.append(padChar);
        }
        sb.append(str);
        return sb.toString();
    }

    /**
     * 补足字符串以满足最小长度 StringUtil.padEnd("1", 3, '0');//"100"
     *
     * @param str 字符串
     * @param minLength 最小长度
     * @param padChar 补充的字符
     * @return 补充后的字符串
     */
    public static String padEnd(String str, int minLength, char padChar) {
        if (str.length() >= minLength) {
            return str;
        }
        StringBuilder sb = new StringBuilder(minLength);
        sb.append(str);
        for (int i = str.length(); i < minLength; i++) {
            sb.append(padChar);
        }
        return sb.toString();
    }

    /**
     * 创建StringBuilder对象
     */
    public static StringBuilder builder() {
        return new StringBuilder();
    }

    /**
     * 创建StringBuilder对象
     */
    public static StringBuilder builder(int capacity) {
        return new StringBuilder(capacity);
    }

    /**
     * 创建StringBuilder对象
     */
    public static StringBuilder builder(String... strs) {
        final StringBuilder sb = new StringBuilder();
        for (String str : strs) {
            sb.append(str);
        }
        return sb;
    }

    /**
     * 获得StringReader
     *
     * @param str 字符串
     * @return StringReader
     */
    public static StringReader getReader(String str) {
        return new StringReader(str);
    }

    /**
     * 获得StringWriter
     *
     * @return StringWriter
     */
    public static StringWriter getWriter() {
        return new StringWriter();
    }

    /**
     * 转化字节数组为16进制字符串.
     */
    public static String toHexString(byte[] bytes) {
        char[] chars = new char[bytes.length * 2];
        int i = 0;
        for (byte b : bytes) {
            chars[i++] = CharUtil.int2hex((b & 0xF0) >> 4);
            chars[i++] = CharUtil.int2hex(b & 0x0F);
        }
        return new String(chars);
    }

    /**
     * IP地址字符串转换为整数
     */
    public static long ip2Long(String ip) {
        String[] tokens = ip.split("[.]");
        if (tokens.length != 4) {
            throw new RuntimeException(format("'{}' 不合法的 IPv4 地址", ip));
        }
        long result = 0;
        for (String token : tokens) {
            try {
                result = (result << 8) | Integer.parseInt(token);
            } catch (NumberFormatException e) {
                throw new RuntimeException("Incorrect number", e);
            }
        }
        return result;
    }

    /**
     * 整数转换为IP地址字符串
     */
    public static String long2Ip(long ip) {
        StringBuilder result = new StringBuilder(15);
        result.append((ip >> 24 & 0xff)).append(".");
        result.append((ip >> 16 & 0xff)).append(".");
        result.append((ip >> 8 & 0xff)).append(".");
        result.append(ip & 0xff);

        return result.toString();
    }

    /**
     * 生成 UUID
     */
    public static String uuid() {
        return java.util.UUID.randomUUID().toString();
    }

    /**
     * 生成 UUID, 不含"<code>-</code>"
     */
    public static String uuidWithoutHyphen() {
        String uuid = java.util.UUID.randomUUID().toString();
        return uuid.replaceAll("-", "");
    }

    /**
     * 验证是否是日期格式
     * @param dateStr
     * @return
     */
    public static boolean isDate(String dateStr) {
        try {
            Date date = new SimpleDateFormat("yyyy-MM-dd").parse(trim(dateStr));
            return true;
        } catch (Exception e) {

        }
        return false;
    }

    /**
     * 验证是否是日期时间格式
     * @param datetimeStr
     * @return
     */
    public static boolean isDateTime(String datetimeStr) {
        try {
            Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(trim(datetimeStr));
            return true;
        } catch (Exception e) {

        }
        return false;
    }

    /**
     * 验证是否匹配指定的正则表达式
     * @param text
     * @param regEx
     * @return
     */
    public static boolean matchRegEx(String text, String regEx) {
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(text);
        return m.find();
    }

    /**
     * 验证该字符串是否是数字
     */
    public static boolean isNumber(String str) {
        char[] chars = str.toCharArray();
        boolean isNumber = false;
        boolean dotInserted = false;

        if ("".equals(str)) {
            return false;
        }

        // if the last char in the string is a dot, it isn't a number!
        if (str.charAt(str.length() - 1) == '.') {
            return false;
        }

        for (int i = 0; i < chars.length; i++) {
            // a dot inside the string can be a number as well
            // although it can only occur once.
            if (chars[i] == 46) {
                if (!dotInserted) {
                    dotInserted = true;
                } else {
                    return false;
                }
                // 48 is ASCII for 0, 57 for 9
            } else if (chars[i] < 48 || chars[i] > 57) {
                return false;
            } else {
                isNumber = true;
            }
        }

        return isNumber;
    }

    /**
     * 是否指 int 类型
     */
    public static boolean isInt(String str) {
        if (StringUtil.isBlank(str)) {
            return false;
        }

        try {
            Integer.parseInt(str);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    /**
     * 是否是 float
     */
    public static boolean isFloat(String str) {
        if (StringUtil.isBlank(str)) {
            return false;
        }

        try {
            Float.parseFloat(str);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    /**
     * 是否是 long
     */
    public static boolean isLong(String str) {
        if (StringUtil.isBlank(str)) {
            return false;
        }

        try {
            Long.parseLong(str);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    /**
     * 是否是 double
     */
    public static boolean isDouble(String str) {
        if (StringUtil.isBlank(str)) {
            return false;
        }

        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    /**
     * 转为 double 型
     */
    public static double castDouble(String str) {
        return castDouble(str, 0);
    }

    /**
     * 转为 double 型（提供默认值）
     */
    public static double castDouble(String str, double defaultValue) {
        double doubleValue = defaultValue;
        if (StringUtil.isNotEmpty(str)) {
            try {
                doubleValue = Double.parseDouble(str);
            } catch (NumberFormatException e) {
                doubleValue = defaultValue;
            }
        }
        return doubleValue;
    }

    /**
     * 转为 long 型
     */
    public static long castLong(String str) {
        return castLong(str, 0);
    }

    /**
     * 转为 long 型（提供默认值）
     */
    public static long castLong(String str, long defaultValue) {
        long longValue = defaultValue;
        if (StringUtil.isNotEmpty(str)) {
            try {
                longValue = Long.parseLong(str);
            } catch (NumberFormatException e) {
                longValue = defaultValue;
            }
        }
        return longValue;
    }

    /**
     * 转为 int 型
     */
    public static int castInt(String str) {
        return castInt(str, 0);
    }

    /**
     * 转为 int 型（提供默认值）
     */
    public static int castInt(String str, int defaultValue) {
        int intValue = defaultValue;
        if (StringUtil.isNotEmpty(str)) {
            try {
                intValue = Integer.parseInt(str);
            } catch (NumberFormatException e) {
                intValue = defaultValue;
            }
        }
        return intValue;
    }

    /**
     * 转为 boolean 型
     */
    public static boolean castBoolean(String str) {
        return castBoolean(str, false);
    }

    /**
     * 转为 boolean 型（提供默认值）
     */
    public static boolean castBoolean(String str, boolean defaultValue) {
        boolean booleanValue = defaultValue;
        if (StringUtil.isNotEmpty(str)) {
            booleanValue = Boolean.parseBoolean(str);
        }
        return booleanValue;
    }
}
